package cn.com.w.docx4j.demo.util;

import lombok.extern.slf4j.Slf4j;
import org.docx4j.Docx4J;
import org.docx4j.XmlUtils;
import org.docx4j.convert.out.FOSettings;
import org.docx4j.fonts.IdentityPlusMapper;
import org.docx4j.fonts.Mapper;
import org.docx4j.fonts.PhysicalFonts;
import org.docx4j.jaxb.Context;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.wml.Document;
import org.springframework.util.CollectionUtils;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
import java.util.regex.Pattern;

@Slf4j
public final class Docx4jUtils {

    /**
     * 替换变量并输出word文档
     *
     * @param inputStream
     * @param map
     * @param outputStream
     */
    public static void replaceDocUseDoc4j(InputStream inputStream, Map<String, String> map, OutputStream outputStream) {
        try {
            WordprocessingMLPackage doc = WordprocessingMLPackage.load(inputStream);
            MainDocumentPart mainDocumentPart = doc.getMainDocumentPart();
            if (!CollectionUtils.isEmpty(map)) {
                Docx4jUtils.cleanDocumentPart(mainDocumentPart);
                mainDocumentPart.variableReplace(map);
            }
            // 输出word文件
            doc.save(outputStream);
            outputStream.flush();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 替换变量并输出PDF文档
     *
     * @param inputStream
     * @param map
     * @param outputStream
     */
//    public static void replaceDocOutputPDF(InputStream inputStream, Map<String, String> map,
//                                           OutputStream outputStream) {
//        try {
//            WordprocessingMLPackage doc = WordprocessingMLPackage.load(inputStream);
//            MainDocumentPart mainDocumentPart = doc.getMainDocumentPart();
//            if (!CollectionUtils.isEmpty(map)) {
//                Docx4jUtils.cleanDocumentPart(mainDocumentPart);
//                mainDocumentPart.variableReplace(map);
//            }
//
//            setFontMapper(doc);
//            FOSettings foSettings = Docx4J.createFOSettings();
//            foSettings.setWmlPackage(doc);
//            Docx4J.toFO(foSettings, outputStream, Docx4J.FLAG_EXPORT_PREFER_XSL);
//            outputStream.flush();
//        } catch (Exception e) {
//            log.error(e.getMessage(), e);
//        }
//    }

//    private static void setFontMapper(WordprocessingMLPackage mlPackage) throws Exception {
//        Mapper fontMapper = new IdentityPlusMapper();
//        fontMapper.put("隶书", PhysicalFonts.get("LiSu"));
//        fontMapper.put("宋体", PhysicalFonts.get("SimSun"));
//        fontMapper.put("微软雅黑", PhysicalFonts.get("Microsoft Yahei"));
//        fontMapper.put("黑体", PhysicalFonts.get("SimHei"));
//        fontMapper.put("楷体", PhysicalFonts.get("KaiTi"));
//        fontMapper.put("新宋体", PhysicalFonts.get("NSimSun"));
//        fontMapper.put("华文行楷", PhysicalFonts.get("STXingkai"));
//        fontMapper.put("华文仿宋", PhysicalFonts.get("STFangsong"));
//        fontMapper.put("仿宋", PhysicalFonts.get("FangSong"));
//        fontMapper.put("幼圆", PhysicalFonts.get("YouYuan"));
//        fontMapper.put("华文宋体", PhysicalFonts.get("STSong"));
//        fontMapper.put("华文中宋", PhysicalFonts.get("STZhongsong"));
//        fontMapper.put("等线", PhysicalFonts.get("SimSun"));
//        fontMapper.put("等线 Light", PhysicalFonts.get("SimSun"));
//        fontMapper.put("华文琥珀", PhysicalFonts.get("STHupo"));
//        fontMapper.put("华文隶书", PhysicalFonts.get("STLiti"));
//        fontMapper.put("华文新魏", PhysicalFonts.get("STXinwei"));
//        fontMapper.put("华文彩云", PhysicalFonts.get("STCaiyun"));
//        fontMapper.put("方正姚体", PhysicalFonts.get("FZYaoti"));
//        fontMapper.put("方正舒体", PhysicalFonts.get("FZShuTi"));
//        fontMapper.put("华文细黑", PhysicalFonts.get("STXihei"));
//        fontMapper.put("宋体扩展", PhysicalFonts.get("simsun-extB"));
//        fontMapper.put("仿宋_GB2312", PhysicalFonts.get("FangSong_GB2312"));
//        fontMapper.put("新細明體", PhysicalFonts.get("SimSun"));
//        //解决宋体（正文）和宋体（标题）的乱码问题
//        PhysicalFonts.put("PMingLiU", PhysicalFonts.get("SimSun"));
//        PhysicalFonts.put("新細明體", PhysicalFonts.get("SimSun"));
//
//        mlPackage.setFontMapper(fontMapper);
//    }

    public static boolean cleanDocumentPart(MainDocumentPart documentPart) throws Exception {
        if (documentPart == null) return false;

        Document document = documentPart.getContents();
        String wmlTemplate = XmlUtils.marshaltoString(document, true, false, Context.jc);
        document = (Document) XmlUtils.unwrap(DocxVariableClearUtils.doCleanDocumentPart(wmlTemplate, Context.jc));
        documentPart.setContents(document);
        return true;
    }

    private static class DocxVariableClearUtils {

        /**
         * 去任意XML标签
         */
        private static final Pattern XML_PATTERN = Pattern.compile("<[^>]*>");

        private DocxVariableClearUtils() {
        }

        /**
         * start符号
         */
        private static final char PREFIX = '$';

        /**
         * 中包含
         */
        private static final char LEFT_BRACE = '{';

        /**
         * 结尾
         */
        private static final char RIGHT_BRACE = '}';

        /**
         * 未开始
         */
        private static final int NONE_START = -1;

        /**
         * 未开始
         */
        private static final int NONE_START_INDEX = -1;

        /**
         * 开始
         */
        private static final int PREFIX_STATUS = 1;

        /**
         * 左括号
         */
        private static final int LEFT_BRACE_STATUS = 2;

        /**
         * 右括号
         */
        private static final int RIGHT_BRACE_STATUS = 3;


        /**
         * doCleanDocumentPart
         *
         * @param wmlTemplate
         * @param jc
         * @return
         * @throws JAXBException
         */
        private static Object doCleanDocumentPart(String wmlTemplate, JAXBContext jc) throws JAXBException {
            // 进入变量块位置
            int curStatus = NONE_START;
            // 开始位置
            int keyStartIndex = NONE_START_INDEX;
            // 当前位置
            int curIndex = 0;
            char[] textCharacters = wmlTemplate.toCharArray();
            StringBuilder documentBuilder = new StringBuilder(textCharacters.length);
            documentBuilder.append(textCharacters);
            // 新文档
            StringBuilder newDocumentBuilder = new StringBuilder(textCharacters.length);
            // 最后一次写位置
            int lastWriteIndex = 0;
            for (char c : textCharacters) {
                switch (c) {
                    case PREFIX:
                        // TODO 不管其何状态直接修改指针,这也意味着变量名称里面不能有PREFIX
                        keyStartIndex = curIndex;
                        curStatus = PREFIX_STATUS;
                        break;
                    case LEFT_BRACE:
                        if (curStatus == PREFIX_STATUS) {
                            curStatus = LEFT_BRACE_STATUS;
                        }
                        break;
                    case RIGHT_BRACE:
                        if (curStatus == LEFT_BRACE_STATUS) {
                            // 接上之前的字符
                            newDocumentBuilder.append(documentBuilder.substring(lastWriteIndex, keyStartIndex));
                            // 结束位置
                            int keyEndIndex = curIndex + 1;
                            // 替换
                            String rawKey = documentBuilder.substring(keyStartIndex, keyEndIndex);
                            // 干掉多余标签
                            String mappingKey = XML_PATTERN.matcher(rawKey).replaceAll("");
                            if (!mappingKey.equals(rawKey)) {
                                char[] rawKeyChars = rawKey.toCharArray();
                                // 保留原格式
                                StringBuilder rawStringBuilder = new StringBuilder(rawKey.length());
                                // 去掉变量引用字符
                                for (char rawChar : rawKeyChars) {
                                    if (rawChar == PREFIX || rawChar == LEFT_BRACE || rawChar == RIGHT_BRACE) {
                                        continue;
                                    }
                                    rawStringBuilder.append(rawChar);
                                }
                                // FIXME 要求变量连在一起
                                String variable = mappingKey.substring(2, mappingKey.length() - 1);
                                int variableStart = rawStringBuilder.indexOf(variable);
                                if (variableStart > 0) {
                                    rawStringBuilder = rawStringBuilder.replace(variableStart, variableStart + variable.length(), mappingKey);
                                }
                                newDocumentBuilder.append(rawStringBuilder.toString());
                            } else {
                                newDocumentBuilder.append(mappingKey);
                            }
                            lastWriteIndex = keyEndIndex;

                            curStatus = NONE_START;
                            keyStartIndex = NONE_START_INDEX;
                        }
                    default:
                        break;
                }
                curIndex++;
            }
            // 余部
            if (lastWriteIndex < documentBuilder.length())
                newDocumentBuilder.append(documentBuilder.substring(lastWriteIndex));
            return XmlUtils.unmarshalString(newDocumentBuilder.toString(), jc);
        }

    }
}
